#####
# VERY simple boot loader or X86 machines
# Taken almost verbatim from: 
#   http://www.websofia.com/2011/10/writing-your-own-boot-loader-for-a-toy-operating-system-1/
# This file contains some functions used by this boot loader.
# Programmer: Pete Lutz
# Date: 5-10-2012
#####

#####
# W r i t e S t r i n g
# Function to print a string on the console using  interrupt 0x10
#####
		.func	WriteString
WriteString:
		lodsb			# load byte at ds:si into al ... incr si
		or	al, al		# test of character is 0 (end)
		jz	WriteString_end	# end on zero character

		mov	ah, 0xe		# subfunction 0xe for int 0x10 is print
		mov	bx, 9		# bh = 0x0    bl = 0x9 (white)
		int	0x10		# print char in al
		jmp	WriteString	# loop
WriteString_end:
		retw			# return
		.endfunc

#####
# R e b o o t
# Reboot the computer
#####
		.func	Reboot
Reboot:
		lea	si, rebootmsg	# load address of message
		call 	WriteString
		xor 	ax, ax		# clear ax
		int	0x16		# call bios to wait for any key

		.byte	0xea		# jump to address in next 2 words
		.word	0x0000		# address = ffff:0000
		.word	0xffff
		.endfunc

#####
# R e a d S e c t o r
# Read sector with logical address (LBA) AX into data
# buffer at ES:BX. This function uses interrupt 13h, subfunction ah=2.
#####
		.func ReadSector
ReadSector:
		xor     cx, cx		# Set try count = 0
 
readsect:
		push    ax		# Store logical block addr
		push    cx		# Store try number
		push    bx		# Store data buffer offset

		# Calculate cylinder, head and sector:
		# Cylinder = (LBA / SectorsPerTrack) / NumHeads
		# Sector   = (LBA mod SectorsPerTrack) + 1
		# Head     = (LBA / SectorsPerTrack) mod NumHeads

		mov     bx, iTrackSect	# Get sectors per track
		xor     dx, dx
		div     bx		# Divide (dx:ax/bx to ax,dx)
					# Quotient (ax) =  LBA / SectorsPerTrack
					# Remainder (dx) = LBA mod SectorsPerTrack
		inc     dx		# Add 1 to remainder, since sectors 
					# start at 1
		mov     cl, dl		# Store result in cl for int 13h call.

		mov     bx, iHeadCnt	# Get number of heads
		xor     dx, dx
		div     bx		# Divide (dx:ax/bx to ax,dx)
					# Quotient (ax) = Cylinder
					# Remainder (dx) = head
		mov     ch, al		# ch = cylinder                      
		xchg    dl, dh		# dh = head number

		# Call interrupt 0x13, subfunction 2 to actually
		# read the sector.
		# al = number of sectors
		# ah = subfunction 2
		# cx = sector number
		# dh = head number
		# dl = drive number
		# es:bx = data buffer
		# If it fails, the carry flag will be set.
		mov     ax, 0x0201	# Subfunction 2, read 1 sector
		mov     dl, iBootDrive	# from this drive
		pop     bx		# Restore data buffer offset.
		int     0x13
		jc      readfail

		# On success, return to caller.
		pop     cx		# Discard try number
		pop     ax		# Get logical block from stack
		ret

		# The read has failed.
		# We will retry four times total, then jump to boot failure.
readfail:   
		pop     cx		# Get try number             
		inc     cx		# Next try
		cmp     cx, word ptr 4	# Stop at 4 tries
		je      bootFailure
 
		# Reset the disk system:
		xor     ax, ax
		int     0x13

		# Get logical block from stack and retry.
		pop     ax
		jmp     readsect
		.endfunc
